<?php

	/**
	 * Singular abstract class.
	 *
	 * @author Rashid Moahamd <rashid2538@gmail.com>
	 * @version 1.0
	 * @copyright Rashid
	 */
	abstract class AbstractPlural {

		protected $_dbClassPrefix = '';
		protected $_dbServer = '';
		protected $_matchedRecords = array();
		protected $_resultCount = 0;
		protected $_totalCount = 0;
		protected $_totalPages = 0;
		protected $_dbClass = '';
		protected $_pageNumber = 1;
		protected $_sortColumn = '';
		protected $_sortOrder = 'asc';

		public function __construct( $firstArgument = false ) {
			$this->_dbClassPrefix = explode( '_', get_class( $this ) );
			$this->_dbServer = strtolower( $this->_dbClassPrefix[ 0 ] );
			$this->_dbClassPrefix = $this->_dbClassPrefix[ 0 ] . '_Db_';
			if( func_num_args() > 0 ) {
				if( is_array( $firstArgument ) ) {
					if( $this->areMyOwn( $firstArgument ) ) {
						$singularClass = $this->_dbServer . '_' . lcfirst( $this->_dbClass );
						$this->_matchedRecords = array();
						foreach( $firstArgument as $expectedObject ) {
							if( is_a( $expectedObject, $singularClass ) ) {
								$this->_matchedRecords[] = $expectedObject;
							}
						}
						$this->_totalCount = $this->_resultCount = count( $this->_matchedRecords );
						$this->_totalPages = 1;
					} else {
						$argumentString = implode( '", "', $firstArgument );
						$dbClass = $this->_dbServer . '_' . lcfirst( $this->_dbClass );
						$selfDbObj = getDb( $dbClass );
						eval( '$selfDbObj->set( "' . $argumentString . '" );' );
						$this->_matchedRecords = array();
						$this->_resultCount = $selfDbObj->getResultCount();
						$this->_pageNumber = $selfDbObj->getPageNumber();
						for( $i = 0; $i < $this->_resultCount; $i++ ) {
							$this->_matchedRecords[] = getSingle( $dbClass, $selfDbObj->getRecords( $i ) );
						}
						$this->_totalCount = $selfDbObj->getTotalCount();
						$this->_totalPages = $selfDbObj->getTotalPages();
					}
				} else {
					$functionArguments = func_get_args();
					$argumentString = implode( '", "', $functionArguments );
					$dbClass = $this->_dbServer . '_' . lcfirst( $this->_dbClass );
					$selfDbObj = getDb( $dbClass );
					eval( '$selfDbObj->set( "' . $argumentString . '" );' );
					$this->_matchedRecords = array();
					$this->_resultCount = $selfDbObj->getResultCount();
					$this->_pageNumber = $selfDbObj->getPageNumber();
					for( $i = 0; $i < $this->_resultCount; $i++ ) {
						$this->_matchedRecords[] = getSingle( $dbClass, $selfDbObj->getRecords( $i ) );
					}
					$this->_totalCount = $selfDbObj->getTotalCount();
					$this->_totalPages = $selfDbObj->getTotalPages();
				}
			}
		}
	
		public function mergeObjects() {
			$primaryIds = array();
			$records = $this->_matchedRecords;
			$itsObjects = func_get_args();
			$objectsCount = count( $itsObjects );
			$foreignKey = false;
			foreach( $this() as $singularObj ) {
				$foreignKey = !$foreignKey ? ucfirst( $singularObj->getForeignKey() ) : $foreignKey;
				$primaryIds[] = $singularObj->{ 'get' . $foreignKey}();
			}
			for( $i = 0; $i < $objectsCount; $i++ ) {
				$pluralObj = $itsObjects[ $i ];
				foreach( $pluralObj() as $singularObj ) {
					$foreignKey = !$foreignKey ? ucfirst( $singularObj->getForeignKey() ) : $foreignKey;
					if( array_search( $singularObj->{ 'get' . $foreignKey}(), $primaryIds ) === false ) {
						$primaryIds[] = $singularObj->{ 'get' . $foreignKey}();
						$records[] = $singularObj;
					}
				}
			}
			$this->_resultCount = count( $records );
			$this->_totalCount = count( $records );
			$this->_totalPages = 1;
			$this->_matchedRecords = $records;
			return $this;
		}

		private function compareSingles( $firstSingle, $secondSingle ) {
			$runFunction = 'get' . ucfirst( $this->_sortColumn );
			$firstValue = strtolower( $firstSingle->$runFunction() );
			$secondValue = strtolower( $secondSingle->$runFunction() );
			if( $firstValue == $secondValue ) {
				return 0;
			}
			if( $this->_sortOrder == 'asc' ) {
				return( $firstValue < $secondValue ) ? -1 : 1;
			} else {
				return( $firstValue > $secondValue ) ? -1 : 1;
			}
		}

		public function sortOn( $column, $order = 'asc' ) {
			$this->_sortColumn = $column;
			$this->_sortOrder = $order;
			usort( $this->_matchedRecords, array( $this, 'compareSingles' ) );
			return $this;
		}

		public function updateStats( $totalCount, $totalPages ) {
			$this->_totalCount = $totalCount;
			$this->_totalPages = $totalPages;
		}

		private function areMyOwn( $arrayOfObjects ) {
			$singularClass = str_replace( 'Db_', '', $this->_dbClassPrefix ) . $this->_dbClass;
			foreach( $arrayOfObjects as $expectedObject ) {
				if( !is_a( $expectedObject, $singularClass ) ) {
					return false;
				}
			}
			return true;
		}

		public function get() {
			$argumentsArray = func_get_args();
			$argumentsCount = func_num_args();
			if( $argumentsCount == 1 && is_numeric( $argumentsArray[ 0 ] ) ) {
				if( $this->_resultCount > $argumentsArray[ 0 ] ) {
					return $this->_matchedRecords[ $argumentsArray[ 0 ] ];
				} else {
					return '';
				}
			} else if( $argumentsCount == 1 ) {
				$singularObj = $this->get( 0 );
				$query = 'get' . ucfirst( $argumentsArray[ 0 ] );
				return $singularObj->$query();
			} else if( $argumentsCount > 1 ) {
				$index = $argumentsArray[ 1 ];
				$query = 'get' . ucfirst( $argumentsArray[ 0 ] );
				if( $this->_resultCount > $index ) {
					$singularObj = $this->get( $index );
					return $singularObj->$query();
				} else {
					throw new Exception( 'No such record found!' );
				}
			}
		}

		public function getPageNumber() {
			return $this->_pageNumber;
		}

		public function __call( $functionName, $argumentsArray ) {
			$checkForName = "get{$this->_dbClass}s";
			if( $functionName == $checkForName ) {
				return $this->_matchedRecords;
			} else {
				if( count( $argumentsArray ) == 1 && is_numeric( $argumentsArray[ 0 ] ) ) {
					if( $this->_resultCount > $argumentsArray[ 0 ] ) {
						$singularObj = $this->_matchedRecords[ $argumentsArray[ 0 ] ];
						return $singularObj->$functionName();
					} else {
						return '';
					}
				}
				return $this->$functionName( 0 );
			}
			throw new Exception( "Call to undefined function '$functionName'!" );
		}

		public function update() {
			$updateFields = func_get_args();
			foreach( $this->_matchedRecords as $i => $singularObj ) {
				call_user_func_array( array( $singularObj, 'update' ), $updateFields );
			}
			return $this;
		}

		public function delete() {
			foreach( $this->_matchedRecords as $i => $singularObj ) {
				call_user_func( array( $singularObj, 'delete' ) );
			}
			return $this;
		}

		public function varDump() {
			$return = array();
			foreach( $this->_matchedRecords as $singularObj ) {
				$return[] = $singularObj->varDump();
			}
			return $return;
		}

		public function __invoke() {
			return $this->_matchedRecords;
		}

		public function getResultCount() {
			return $this->_resultCount;
		}

		public function getTotalCount() {
			return $this->_totalCount;
		}

		public function getTotalPages() {
			return $this->_totalPages;
		}

		public function getPaging( $pagePath = 'page/' ) {
			$temp_paging = array();
			$currentPageNumber = $this->getPageNumber();
			if( $this->getTotalPages() > 1 ) {
				$p_start = 1;
				if( $currentPageNumber > 5 ) {
					$p_start = $currentPageNumber - 5;
				}
				$p_end = $p_start + 9;
				if( $p_end > $this->getTotalPages() ) {
					$p_end = $this->getTotalPages();
				}
				$i = 0;
				if( $currentPageNumber > 1 ) {
					$temp_paging_link = HOME_PATH . $pagePath . ( $currentPageNumber - 1 ) . "/";
					if ( 2 == $currentPageNumber and $pagePath == 'page/' ) {
						$temp_paging_link = HOME_PATH;
					}
					$temp_paging[$i]['numbers'] = "Prev";
					$temp_paging[$i]['link'] = $temp_paging_link;
					$temp_paging[$i]['isActive'] = false;
					$i++;
				}
				for( $x = $p_start; $x <= $p_end; $x++ ) {
					$temp_paging_link = HOME_PATH . $pagePath . $x . "/";
					if( 1 == $x and $pagePath == 'page/' ) {
						$temp_paging_link = HOME_PATH;
					}
					if( $currentPageNumber != $x ) {
						$temp_paging[$i]['numbers'] = $x;
						$temp_paging[$i]['link'] = $temp_paging_link;
						$temp_paging[$i]['isActive'] = false;
					} else {
						$temp_paging[$i]['numbers'] = $x;
						$temp_paging[$i]['link'] = "";
						$temp_paging[$i]['isActive'] = true;
					}
					$i++;
				}
				if( $currentPageNumber < $p_end ) {
					$temp_paging_link = HOME_PATH . $pagePath . ( $currentPageNumber + 1 ) . "/";
					$temp_paging[$i]['numbers'] = "Next";
					$temp_paging[$i]['link'] = $temp_paging_link;
					$temp_paging[$i]['isActive'] = false;
				}
			}
			return $temp_paging;
		}
	}
